package org.third.customer.csa;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpPrincipal;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsParameters;
import com.sun.net.httpserver.HttpsServer;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManagerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

public class SimpleHTTPSServer {

    static Logger logger = null;
    // static final String DATE_FORMAT = "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n";
    static final String DATE_FORMAT = "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%4$s: %5$s%6$s%n";

    public static final void initLogger(Level logLevel) {
        if (logger != null) {
            return;
        }
        logger = Logger.getLogger(SimpleHTTPSServer.class.getName());
        Logger.getGlobal().getParent().setLevel(logLevel);
        ConsoleHandler consoleHandler = new ConsoleHandler();
        consoleHandler.setLevel(logLevel);
        consoleHandler.setFormatter(new Formatter() {
            private final Date dat = new Date();

            @Override
            public String format(LogRecord record) {
                // if (logLevel.intValue() < Level.INFO.intValue()) {
                dat.setTime(record.getMillis());
                String source;
                if (record.getSourceClassName() != null) {
                    source = record.getSourceClassName();
                    if (record.getSourceMethodName() != null) {
                        source += " " + record.getSourceMethodName();
                    }
                } else {
                    source = record.getLoggerName();
                }
                String message = formatMessage(record);
                String throwable = "";
                if (record.getThrown() != null) {
                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw);
                    pw.println();
                    record.getThrown().printStackTrace(pw);
                    pw.close();
                    throwable = sw.toString();
                }
                return String.format(DATE_FORMAT, dat, source, record.getLoggerName(), record.getLevel().getName(), message, throwable);
                // }
                // else {
                // return record.getMessage() + "\n";
                // }
            }
        });
        Logger logger1 = logger;
        Logger rootLogger = logger;
        while ((null != logger1)) {
            for (Handler h : logger1.getHandlers()) {
                logger1.removeHandler(h);
            }
            if (null == logger1.getParent()) {
                rootLogger = logger1;
            }
            logger1 = logger1.getParent();
        }
        rootLogger.addHandler(consoleHandler);
    }

    static class MyHandler implements HttpHandler {
        public void handle(HttpExchange t) throws IOException {
            logger.info("\n==========Http Request=========");
            Headers headers = t.getRequestHeaders();
            URI requestUri = t.getRequestURI();
            System.out.println(t.getRequestMethod() + " " + requestUri);
            System.out.println("Headers:");
            System.out.println("  Remote: " + t.getRemoteAddress() + ", Local:" + t.getLocalAddress());
            headers.forEach((k, v) -> {
                System.out.println("  " + k + ": " + v);
            });

            if ("GET".equalsIgnoreCase(t.getRequestMethod())) {

            } else if ("POST".equalsIgnoreCase(t.getRequestMethod()) || "PUT".equalsIgnoreCase(t.getRequestMethod())
                    || "PATCH".equalsIgnoreCase(t.getRequestMethod())) {
                HttpPrincipal principal = t.getPrincipal();

                System.out.println("Bodys:");
                InputStream requestBodyInputStream = t.getRequestBody();
                String requestBody = copyToString(requestBodyInputStream, Charset.forName("UTF-8"));
                System.out.println(requestBody);
            } else {

            }
            String response = "\r\n  Remote: " + t.getRemoteAddress() + ", Local:" + t.getLocalAddress() + "\r\n  URL:" + requestUri
                    + "\r\n  {" + " \"status\": false" + "}";
            t.getResponseHeaders().set("Content-Type", "application/text;charset=UTF-8");
            t.sendResponseHeaders(200, response.length());
            OutputStream os = t.getResponseBody();
            os.write(response.getBytes());
            os.write("\r\n".getBytes());
            os.close();
        }
    }

    static final int BUFFER_SIZE = 1024;

    public static String copyToString(InputStream in, Charset charset) throws IOException {

        StringBuilder out = new StringBuilder();
        InputStreamReader reader = new InputStreamReader(in, charset);
        char[] buffer = new char[BUFFER_SIZE];
        int bytesRead = -1;
        while ((bytesRead = reader.read(buffer)) != -1) {
            out.append(buffer, 0, bytesRead);
        }
        return out.toString();
    }

    static SSLContext sslContext;

    public static void initSslContext() throws Exception {
        sslContext = SSLContext.getInstance("TLS");

        // initialise the keystore
        char[] password = "tomcat1".toCharArray();
        KeyStore ks = KeyStore.getInstance("JKS");
        // FileInputStream fis = new FileInputStream("testkey.jks");
        InputStream fis = SimpleHTTPSServer.class.getResourceAsStream("tomcat.keystore");  // 载入证书
        ks.load(fis, password);

        // setup the key manager factory
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, password);

        // setup the trust manager factory
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(ks);

        // setup the HTTPS context and parameters
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    }

    public static HttpServer createHttpServer(InetSocketAddress address, boolean ssl) throws Exception {
        if (ssl) {
            if (sslContext == null) {
                initSslContext();
            }
            HttpsServer httpServer = HttpsServer.create(address, 0);
            httpServer.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
                public void configure(HttpsParameters params) {
                    try {
                        // initialise the SSL context
                        SSLContext c = SSLContext.getDefault();
                        SSLEngine engine = c.createSSLEngine();
                        params.setNeedClientAuth(false);
                        params.setCipherSuites(engine.getEnabledCipherSuites());
                        params.setProtocols(engine.getEnabledProtocols());

                        // get the default parameters
                        SSLParameters defaultSSLParameters = c.getDefaultSSLParameters();
                        params.setSSLParameters(defaultSSLParameters);

                    } catch (Exception ex) {
                        System.out.println("Failed to create HTTPS port");
                    }
                }
            });
            return httpServer;
        } else {
            HttpServer httpServer = HttpServer.create(address, 0);
            return httpServer;
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        testHttp(args);
    }

    public static void testHttp(String[] args) {
        args = new String[] { "-port", "8080", "-contexts", "idm-service,idm-admin" };
        String[] contexts = new String[] { "idm-service", "idm-admin", "mngr-port" };
        int port = 8080, sslPort = 8443;
        Level level = Level.INFO;
        for (int i = 0, size = args.length; i < size; ++i) {
            if ("-port".equalsIgnoreCase(args[i])) {
                port = Integer.parseInt(args[++i]);
            } else if ("-log".equalsIgnoreCase(args[i])) {
                level = Level.parse(args[++i]);
            } else if ("-sslport".equalsIgnoreCase(args[i])) {
                sslPort = Integer.parseInt(args[++i]);
            } else if ("-contexts".equalsIgnoreCase(args[i])) {
                String contextArgs = args[++i];
                contexts = contextArgs.split("\\,");
            }
        }
        initLogger(level);
        logger.info("\nUsage: \n"+
                "   java -jar " + SimpleHTTPSServer.class.getSimpleName() + ".jar -port httpPort -sslPort sslPort -contexts contextList");

        try {
            // setup the socket address
            InetSocketAddress address = new InetSocketAddress(port);
            HttpServer httpServer = createHttpServer(address, false);
            httpServer.createContext("/", new MyHandler());
            httpServer.setExecutor(Executors.newSingleThreadExecutor()); // creates a default executor
            httpServer.start();
            logger.info(httpServer.getClass().getName() + " started in port: " + port);

            InetSocketAddress sslAddress = new InetSocketAddress(sslPort);
            HttpServer httpsServer = createHttpServer(sslAddress, true);
            httpsServer.createContext("/", new MyHandler());
            httpsServer.setExecutor(Executors.newSingleThreadExecutor()); // creates a default executor
            httpsServer.start();
            logger.info(httpsServer.getClass().getName() + " started in ssl port: " + sslPort);

            StringBuilder contextsList = new StringBuilder();
            for (int i = 0, size = contexts.length; i < size; ++i) {
                contextsList.append((i == 0 ? "/" + contexts[i] : ",/" + contexts[i]));
            }
            logger.info("Available Context As below:" + contextsList);
        } catch (Exception exception) {
            System.out.println("Failed to create HTTPS server on port: " + port + " of localhost");
            exception.printStackTrace();

        }
    }

}